import command.CommandExec
import org.joda.time.DateTime
import scopt.OParser

import java.io.File
import implicits.Implicits._
import org.apache.commons.io.FileUtils
import tool.Pojo.CommandData
import tool.Tool

import scala.collection.immutable.SeqMap
import scala.collection.parallel.CollectionConverters._

/**
 * Created by yz on 23/12/2021
 */

case class Config(dataFile: File = new File("E:\\germline_mutation_database\\test\\Germline_mutation\\workspace\\data.csv"),
                  groupFile: File = new File("E:\\germline_mutation_database\\test\\Germline_mutation\\workspace\\group.csv"),
                  workspaceDir: File = new File("workspace"),
                  outDir: File = new File("E:\\modeling_database\\test\\out")
                 )

object Main {

  def main(args: Array[String]): Unit = {

    val builder = OParser.builder[Config]
    val parser = {
      import builder._
      //i g o
      OParser.sequence(
        programName("scopt"),
        head("scopt", "4.x"),
        opt[File]('i', "input").valueName("<file>").required().action { (x, c) =>
          c.copy(dataFile = x)
        }.text("please input data csv file!"),
        opt[File]('g', "group").valueName("<file>").required().action { (x, c) =>
          c.copy(groupFile = x)
        }.text("please input group csv file!"),
        opt[File]('w', "workspace").valueName("<file>").action { (x, c) =>
          c.copy(workspaceDir = x)
        }.text("please input workspace  directory,default create 'workspace' directory under script execute directory!"),
        opt[File]('o', "out").valueName("<file>").required().action { (x, c) =>
          c.copy(outDir = x)
        }.text("please input out directory!"),
        help("help").text("prints this usage text")
      )
    }

    OParser.parse(parser, args, Config()) match {
      case Some(config) => exec(config)
      case _ =>
    }

    def exec(config: Config) = {
      val startTime = System.currentTimeMillis()
      val dataFile = config.dataFile
      val groupFile = config.groupFile
      val workspaceDir = config.workspaceDir.createDirectoryWhenNoExist
      List("calculate_config.txt", "pr_plot_config.txt", "roc_plot_config.txt", "rfPlot_plot_config.txt",
        "color_solution_1.txt", "pca2D_plot_config.txt", "rocCv_plot_config.txt", "rocCvEach_plot_config.txt").
        foreach { fileName =>
          new File(Tool.configDir, fileName).fileCopyToDir(workspaceDir)
        }
      val roCvPlotConfigFile = new File(workspaceDir, "rocCv_plot_config.txt")
      val roCvEachPlotConfigFile = new File(workspaceDir, "rocCvEach_plot_config.txt")
      val resultDir = config.outDir.createDirectoryWhenNoExist
      val commandExec = CommandExec().exec { b =>
        val command =
          s"""
             |cp ${dataFile.unixPath} data.csv
             |cp ${groupFile.unixPath} group.csv
             |${Tool.rScript} ${new File(Tool.rPath, "roc_calculate.R").unixPath}
             |${Tool.rScript} ${new File(Tool.rPath, "roc_plot.R").unixPath}
             |${Tool.rScript} ${new File(Tool.rPath, "train_test_split.R").unixPath}
             |${"Rscript".condaPath()} ${new File(Tool.rPath, "cv_split.R").unixPath}
             |""".stripMargin
        CommandData(workspaceDir, command)
      }.parExec { b =>
        val lrMap = SeqMap("LR" -> "fs_logistic")
        val rfMap = SeqMap("RF" -> "fs_rf")
        val svmMap = SeqMap("SVM" -> "fs_svm")
        val map = lrMap ++ rfMap ++ svmMap
        map.flatMap { case (prefix, rName) =>
          val subWorkspaceDir = new File(workspaceDir, prefix)
          val command =
            s"""
               |dos2unix *
               |${"Rscript".condaPath()} ${new File(Tool.rPath, s"${rName}.R").unixPath}
               |${"Rscript".condaPath()} ${new File(Tool.rPath, s"${prefix.toLowerCase}_plot.R").unixPath}
               |${"Rscript".condaPath()} ${new File(Tool.rPath, "model_roc_plot.R").unixPath}
               |${"Rscript".condaPath()} ${new File(Tool.rPath, "pr_plot.R").unixPath}
               |""".stripMargin

          val trainFile = new File(workspaceDir, "train.csv")
          val trainGroupFile = new File(workspaceDir, "train_group.csv")
          val pcaCommand = prefix match {
            case "SVM" =>
              s"""
                 |${"Rscript".condaPath()} ${new File(Tool.rPath, "pca_calculate.R").unixPath} --i ${trainFile.unixPath} --g ${trainGroupFile.unixPath}
                 |${"Rscript".condaPath()} ${new File(Tool.rPath, "produce_group.R").unixPath} --g ${trainGroupFile.unixPath}
                 |${"Rscript".condaPath()} ${new File(Tool.rPath, "produce_sample_color.R").unixPath}
                 |${"Rscript".condaPath()} ${new File(Tool.rPath, "pca.R").unixPath}
                 |""".stripMargin
            case _ => ""
          }
          val cvDir = new File(subWorkspaceDir, "cv")
          val cvCommands = {
            val calculateCommand = prefix match {
              case "RF" =>
                s"""
                   |${"Rscript".condaPath()} ${new File(Tool.rPath, "rf_cv.R").unixPath}
                   |""".stripMargin
              case "LR" =>
                s"""
                   |${"Rscript".condaPath()} ${new File(Tool.rPath, "lr_cv.R").unixPath}
                   |""".stripMargin
              case "SVM" =>
                s"""
                   |${"Rscript".condaPath()} ${new File(Tool.rPath, "svm_cv.R").unixPath}
                   |""".stripMargin
              case _ => ""
            }
            val plotCommand = {
              s"""
                 |${"Rscript".condaPath()} ${new File(Tool.rPath, "cv_roc_plot.R").unixPath} --pc ${roCvPlotConfigFile.unixPath}
                 |${"Rscript".condaPath()} ${new File(Tool.rPath, "cv_roc_all_plot.R").unixPath} --pc ${roCvEachPlotConfigFile.unixPath}
                 |""".stripMargin
            }
            List(calculateCommand, plotCommand)
          }
          List(CommandData(subWorkspaceDir, List(command, pcaCommand)), CommandData(cvDir, cvCommands))
        }.toList.par
      }
      if (!commandExec.isSuccess) {
        System.err.println(commandExec.errorInfo)
      } else {
        resultDeal(config)
        println((System.currentTimeMillis() - startTime) / 1000.0)
      }
    }

    def resultDeal(config: Config) = {
      val resultDir = config.outDir.createDirectoryWhenNoExist
      val workspaceDir = config.workspaceDir
      sameDirCopyRecu(workspaceDir, resultDir)
      val map = SeqMap("RF" -> "Random_Forest", "LR" -> "Logistic_Regression", "SVM" -> "Support_Vector_Machine",
        "cv" -> "Cross-Validation-Results")
      map.foreach { case (oldName, newName) =>
        resultDir.allFileAndDirs.filter(_.isDirectory).filter { x =>
          x.getName == oldName
        }.foreach { x =>
          val destDir = new File(x.getParentFile, newName).deleteIfExist
          x.dirMoveTo(destDir)
        }
      }

    }

    def isValidFileName(name: String, excludeSuffixs: List[String] = List.empty) = {
      val suffixs = List(".pdf", ".csv", ".svg", ".html", ".js", ".xlsx", "xls", "gml").filterNot { x =>
        excludeSuffixs.contains(x)
      }
      suffixs.exists(x => name.endsWith(x)) && (!name.startsWith("Rplots"))
    }

    def sameDirCopyRecu(sourceDir: File, targetDir: File) = {

      def loop(file: File): Unit = {
        for (file <- file.listFiles()) {
          if (file.isDirectory) {
            loop(file)
          } else {
            if (isValidFileName(file.getName)) {
              val filePath = file.getAbsolutePath
              val sourcePath = sourceDir.getAbsolutePath
              val targetFile = new File(targetDir, filePath.replace(sourcePath, ""))
              FileUtils.copyFile(file, targetFile)
            }

          }
        }
      }

      loop(sourceDir)

    }


  }


}
